Shader "Perlin/Derivative2"
{
	Properties
	{
		_PermTex2D("PermTex2D", 2D) = "white" {}
		_GradTex2D("GradTex2D", 2D) = "white" {}
		_PermTexSize("PermTexSize", Float) = 256
		
		_UVScale("UVScale", Float) = 4.0
		_UVShift("UVShift", Float) = 0.0
		_NoiseScale("NoiseScale", Float) = 0.5
		_NoiseShift("NoiseShift", Float) = 0.5

		_Octaves("Octaves", Float) = 10
		_Lacunarity("Lacunarity", Float) = 1.92
		_Gain("Gain", Float) = 0.5
	}
	SubShader
	{
		Pass
		{
			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag
			#pragma target 4.0
			#pragma only_renderers d3d11

			#include "UnityCG.cginc"
			#include "Permutation.cginc"
			#include "Gradient.cginc"
			
			float _UVScale;
			float _UVShift;
			float _NoiseScale;
			float _NoiseShift;

			float _Octaves;
			float _Lacunarity;
			float _Gain;

			float2 fade(float2 t)
			{
				return t*t*t*(t*(t*6-15)+10);
			}

			float2 deriv(float2 x)
			{
				return x*x*(x*(30*x-60)+30);
			}

			float3 perlinNoiseDerivative2(float2 xy)
			{
				// Find unit square that contains point and find relative x,y of point in square
				float2 ij = floor(xy);
				xy -= ij;
				
				// Compute fade curves for each of x,y
				float2 uv = fade(xy);

				// Compute derivative curves for each of x,y
				float2 dxy = deriv(xy);
				
				// Hash coordinates at the 4 square corners
				float4 perm = perm2(ij);

				// Sample gradients at the 4 square corners (normalised to the range [-1, 1])
				float4 g0 = grad2(perm.xy);
				float4 g1 = grad2(perm.zw);

				// Compute gradient ramp at the 4 square corners
				float g00 = dot2(g0.xy, xy+float2( 0,  0));
				float g10 = dot2(g0.zw, xy+float2(-1,  0));
				float g01 = dot2(g1.xy, xy+float2( 0, -1));
				float g11 = dot2(g1.zw, xy+float2(-1, -1));

				// Blend the results along x
				float nx0 = lerp(g00, g10, uv.x);
				float nx1 = lerp(g01, g11, uv.x);

				// Blend the results along y
				float nxy = lerp(nx0, nx1, uv.y);

				// Compute derivatives
				float dx = dxy.x*((g10-g00)+(g00-g10-g01+g11)*uv.y);
				float dy = dxy.y*((g01-g00)+(g00-g10-g01+g11)*uv.x);

				// Return the result, roughly normalised to the range [-1, 1]
				// and return the pseudo dn/dx and dn/dy, scaled by the same factor
				return float3(nxy, dx, dy);
			}

			float perlinNoiseDerivative2(float2 xy, int octaves, float lacunarity, float gain)
			{
				float sum = 0.0f;
				float freq = 1.0f;
				float amp = 1.0f;
				float2 dsum = float2(0, 0);

				// Sum noise octaves
				for (int i=0; i<octaves; i++)
				{
					float3 n = perlinNoiseDerivative2(xy*freq);
					dsum += n.yz;
					sum += amp*n.x/(1+dot(dsum, dsum));
					freq *= lacunarity;
					amp *= gain;
				}

				// Return the result
				return sum;
			}

			struct v2f
			{
				float4 pos : POSITION;
				float2 uv : TEXCOORD0;
			};

			v2f vert(appdata_base v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = v.texcoord.xy;
				return o;
			}

			float4 frag(v2f i) : COLOR
			{
				return perlinNoiseDerivative2(i.uv*_UVScale+_UVShift, _Octaves, _Lacunarity, _Gain)*_NoiseScale+_NoiseShift;
			}

			ENDCG
		}
	}
	Fallback Off
}